package com.seven.http;

import com.seven.commons.HttpClientUtil;
import com.seven.commons.JsonUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.NameValuePair;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.utils.URIBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.DnsResolver;
import org.apache.http.conn.HttpConnectionFactory;
import org.apache.http.conn.ManagedHttpClientConnection;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.DefaultHttpResponseParserFactory;
import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.conn.SystemDefaultDnsResolver;
import org.apache.http.impl.io.DefaultHttpRequestWriterFactory;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * @projectname:common-utils
 * @author: sevenDay
 * @date: 2019/7/24 13:48
 * @desc:
 */
@Component("httpClientComponents")
@ConfigurationProperties(prefix = "seven.http")
@Data
@Slf4j
public class HttpClientComponents {

    /**
     * 最大链接数目
     */
    private Integer maxTotal;

    /**
     * 每个路由最大连接数目
     */
    private Integer perMax;

    /**
     * 连接不活跃验证次数【毫秒为单位】
     */
    private Integer validateTime;

    /**
     * 从连接的超时时间
     */
    private Integer timout;

    private Integer sockectTimeout;

    private Integer connectedTimeOut;

    private Integer maxIdleTime;

    private Integer connTimeToLive;

    private Integer retryCounts;


    private static PoolingHttpClientConnectionManager cm = null;

    private static RequestConfig defaultRequestConfig = null;

    private volatile boolean shutdown = true;

    @PostConstruct
    public void init() {
        Registry<ConnectionSocketFactory> sRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("https", SSLConnectionSocketFactory.getSystemSocketFactory())
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .build();
        HttpConnectionFactory<HttpRoute, ManagedHttpClientConnection> connectionHttpConnectionFactory =
                new ManagedHttpClientConnectionFactory(
                        DefaultHttpRequestWriterFactory.INSTANCE,
                        DefaultHttpResponseParserFactory.INSTANCE
                );
        DnsResolver dnsResolver = SystemDefaultDnsResolver.INSTANCE;

        cm = new PoolingHttpClientConnectionManager(sRegistry, connectionHttpConnectionFactory, dnsResolver);
        //默认为Socket配置
        SocketConfig defaultSocketConfig = SocketConfig.custom().setTcpNoDelay(true).build();
        cm.setDefaultSocketConfig(defaultSocketConfig);
        // 设置最大的连接数
        cm.setMaxTotal(maxTotal);
        // 设置每个路由的基础连接数【默认，每个路由基础上的连接不超过2个，总连接数不能超过20】
        cm.setDefaultMaxPerRoute(perMax);
        //在从连接池获取连接时，连接不活跃多长时间后需要进行一次验证，默认为2s
        cm.setValidateAfterInactivity(validateTime);

        defaultRequestConfig = RequestConfig.custom()
                //设置连接超时时间，2s
                .setConnectTimeout(timout)
                //设置等待数据超时时间，5s
                .setSocketTimeout(sockectTimeout)
                //设置从连接池获取连接的等待超时时间
                .setConnectionRequestTimeout(connectedTimeOut)
                .build();
        Thread a = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    while (!shutdown) {
                        synchronized (this) {
                            wait(5000);
                            cm.closeExpiredConnections();
                            cm.closeIdleConnections(30, TimeUnit.SECONDS);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        a.start();
    }

    @PreDestroy
    public void shutdown() {
        shutdown = true;
        synchronized (this) {
            notifyAll();
        }

    }
    private  CloseableHttpClient getHttpClient() {
        CloseableHttpClient httpClient = HttpClients
                .custom()
                .setConnectionManager(cm)
                //连接池不是共享模式
                .setConnectionManagerShared(false)
                //定期回收空闲连接
                .evictIdleConnections(maxIdleTime, TimeUnit.SECONDS)
                // 定期回收过期连接
                .evictExpiredConnections()
                //连接存活时间，如果不设置，则根据长连接信息决定
                .setConnectionTimeToLive(connTimeToLive, TimeUnit.SECONDS)
                //设置默认请求配置
                .setDefaultRequestConfig(defaultRequestConfig)
                //连接重用策略，即是否能keepAlive
                .setConnectionReuseStrategy(DefaultConnectionReuseStrategy.INSTANCE)
                //长连接配置，即获取长连接生产多长时间
                .setKeepAliveStrategy(DefaultConnectionKeepAliveStrategy.INSTANCE)
                //设置重试次数
                .setRetryHandler(new DefaultHttpRequestRetryHandler(retryCounts, false))
                .build();
        Runtime.getRuntime().addShutdownHook(new Thread(){

            @Override
            public void run() {

                try{
                    if(httpClient !=null){
                        httpClient.close();
                    }
                }catch(IOException e){
                    log.error("error when close httpClient:{}",e);
                }

            }

        });
        return httpClient;
    }


    public <T> T doGet(String url, Map<String, String> param,Class<T> clazz) {
        CloseableHttpClient httpclient = getHttpClient();

        return JsonUtils.jsonToPojo(HttpClientUtil.doGetResponse(url,httpclient,param),clazz);
    }

    public <T> T doGet(String url, Class<T> clazz) {
        return doGet(url, null);
    }

    public <T> T doPost(String url, Map<String, String> param,Class<T> clazz) {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = getHttpClient();

        return JsonUtils.jsonToPojo(HttpClientUtil.doPostResponse(url,httpClient,param),clazz);
    }

    public <T> T doPost(String url,Class<T> clazz) {
        return doPost(url, null);
    }

    public <T> T doPostJson(String url, String json,Class<T> clazz) {
        // 创建Httpclient对象
        CloseableHttpClient httpClient = getHttpClient();

        return JsonUtils.jsonToPojo(HttpClientUtil.doPostJsonResponse(url,httpClient,json),clazz);
    }


}
